SPM-175 주문관리 구현 / 주문 로직 구현#18
Conversation
Walkthrough주문 기능(도메인/DTO/API/레포/유스케이스/DI/ViewModel/UI/리소스) 일괄 추가, 장바구니→주문 흐름 통합(결과 바텀시트), 네비게이션에 order-detail 라우트/routeOrderDetail 추가, 여러 레포지토리·뷰모델의 runCatching → 명시적 try/catch 전환. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant OrderListScreen
participant OrderListViewModel
participant GetOrderUseCase
participant OrderRepository
participant OrderApi
User->>OrderListScreen: 진입
OrderListScreen->>OrderListViewModel: LoadOrderList
OrderListViewModel->>GetOrderUseCase: invoke()
GetOrderUseCase->>OrderRepository: getOrderList()
OrderRepository->>OrderApi: getOrderList()
OrderApi-->>OrderRepository: OrderDto 리스트
OrderRepository-->>GetOrderUseCase: OrderList
GetOrderUseCase-->>OrderListViewModel: OrderList 반환
OrderListViewModel-->>OrderListScreen: uiState 업데이트
User->>OrderListScreen: 항목 클릭
OrderListScreen->>AppNavHost: routeOrderDetail(agencyId, orderId)
sequenceDiagram
participant User
participant CartListScreen
participant CartListViewModel
participant CreateOrderUseCase
participant OrderRepository
participant OrderResultBottomSheet
User->>CartListScreen: 주문 요청
CartListScreen->>CartListViewModel: 주문 이벤트
CartListViewModel->>CreateOrderUseCase: invoke()
CreateOrderUseCase->>OrderRepository: createOrder()
OrderRepository-->>CreateOrderUseCase: OrderList
CreateOrderUseCase-->>CartListViewModel: 결과
CartListViewModel-->>CartListScreen: processedOrder 포함 uiState
CartListScreen->>OrderResultBottomSheet: processedOrder 전달 (표시)
OrderResultBottomSheet->>User: 주문 결과/상세/취소 액션 제공
Estimated code review effort🎯 4 (Complex) | ⏱️ ~55 minutes Possibly related PRs
Suggested reviewers
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 15
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
app/src/main/java/com/sampoom/android/feature/part/ui/PartScreen.kt (1)
72-73: 검색 기능이 작동하지 않는 치명적인 버그문자열 리터럴이 실제 참조 대신 사용되고 있어 검색 기능이 완전히 작동하지 않습니다.
value는 항상 "uiState.searchQuery" 문자열을 표시하고, 입력 변경은 아무 작업도 트리거하지 않습니다.다음 diff를 적용하여 수정하세요:
- value = "uiState.searchQuery", - onValueChange = { "viewModel.onSearchQueryChanged(it)" }, + value = uiState.searchQuery, + onValueChange = { viewModel.onSearchQueryChanged(it) },app/src/main/java/com/sampoom/android/core/network/ErrorHandling.kt (1)
15-23: 에러 바디 로깅은 PII 유출 위험 — 디버그 게이팅/마스킹 필요서버 에러 바디 전체를 Log.d로 그대로 남기면 토큰/식별자 등 민감정보가 로그캣에 노출될 수 있습니다. 디버그 빌드에서만, 그리고 길이 제한/마스킹 후 기록해주세요.
다음과 같이 최소한으로 보완 제안:
- 디버그 게이트: if (Log.isLoggable("ErrorHandling", Log.DEBUG)) { … }
- 길이 제한: val logged = errorBody.take(1024)
- 민감 키(redaction) 마스킹: 정규식으로 "token|email" 등 치환
app/src/main/java/com/sampoom/android/feature/outbound/data/repository/OutboundRepositoryImpl.kt (1)
9-9: Hilt 주입 실패: jakarta.inject 사용 (복수 위치)Hilt는 javax.inject를 사용합니다. jakarta 라이브러리는 호환되지 않으며 주입/빌드가 깨집니다. 다음 두 위치를 모두 javax로 교체하세요.
- app/src/main/java/com/sampoom/android/feature/outbound/data/repository/OutboundRepositoryImpl.kt (라인 9):
jakarta.inject.Inject→javax.inject.Inject- app/src/main/java/com/sampoom/android/feature/order/di/OrderModules.kt (라인 11):
jakarta.inject.Singleton→javax.inject.Singleton- import jakarta.inject.Inject + import javax.inject.Inject- import jakarta.inject.Singleton + import javax.inject.Singleton
🧹 Nitpick comments (20)
app/src/main/res/drawable/ic_check_circle.xml (1)
3-3: 경로 데이터의 중복 명령어 정리 제안pathData의 끝부분에
Q480,480 480,480Q480,480 480,480...와 같이 동일한 좌표를 반복하는 이차 베지에(quadratic Bezier) 명령어들이 있습니다. 이 명령어들은 시각적으로 기여하지 않으며 불필요한 코드로 보입니다. 파일을 정리하기 위해 이 부분을 제거해도 아이콘 모양에는 영향이 없습니다.다음과 같이 정리할 수 있습니다:
- <path android:fillColor="@android:color/white" android:pathData="M424,664L706,382L650,326L424,552L310,438L254,494L424,664ZM480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880ZM480,800Q614,800 707,707Q800,614 800,480Q800,346 707,253Q614,160 480,160Q346,160 253,253Q160,346 160,480Q160,614 253,707Q346,800 480,800Z"/> + <path android:fillColor="@android:color/white" android:pathData="M424,664L706,382L650,326L424,552L310,438L254,494L424,664ZM480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880ZM480,800Q614,800 707,707Q800,614 800,480Q800,346 707,253Q614,160 480,160Q346,160 253,253Q160,346 160,480Q160,614 253,707Q346,800 480,800Z"/>app/src/main/java/com/sampoom/android/feature/part/ui/PartScreen.kt (1)
119-119: 그룹 섹션과의 일관성 검토 필요카테고리 섹션의 ErrorContent와 EmptyContent에
.fillMaxWidth()가 추가되었지만, 그룹 섹션(214, 221번 라인)의 동일한 컴포넌트에는 적용되지 않았습니다. UI 일관성을 위해 그룹 섹션에도 동일한 수정을 적용하는 것이 좋습니다.그룹 섹션에도 동일한 수정을 적용하려면 다음을 추가하세요:
214번 라인:
modifier = Modifier.height(200.dp).fillMaxWidth()221번 라인:
modifier = Modifier.height(200.dp).fillMaxWidth()Also applies to: 126-126
app/src/main/java/com/sampoom/android/feature/order/ui/OrderDetailUiState.kt (1)
5-13: 네이밍/모델링 가독성orderDetail가 List라면 복수 의미가 명확히 드러나지 않습니다. orders 또는 orderLines 등으로의 이름 변경을 고려해주세요. 상세 단일 객체가 필요하다면 별도 OrderDetail 도메인 모델 도입도 검토할 만합니다.
app/src/main/java/com/sampoom/android/feature/order/ui/OrderDetailViewModel.kt (6)
33-41: orderId 설정 흐름API에서 orderId를 나중에 세팅(setOrderIdFromApi)하는 경우 자동 로드가 되지 않습니다. 세팅 시점에 로드까지 트리거하거나, 0L이면 이벤트를 무시하는 가드가 필요합니다.
34-35: 미사용 변수agencyId는 사용되지 않습니다. 제거해 주세요.
58-66: 중복 이벤트 처리 단순화LoadOrder와 RetryOrder가 동일합니다. 하나로 합치거나 RetryOrder를 제거해도 됩니다.
68-93: 다중 로드 경쟁 상태 가능성빠른 연속 탭에서 이전 로드가 살아있을 수 있습니다. 최신 요청만 유효하게 하려면 Job 추적 후 cancel-and-join 패턴을 권장합니다.
예시:
- private var loadJob: Job? = null
- load 호출 시 loadJob?.cancel(); loadJob = viewModelScope.launch { … }
64-65: 에러 클리어 일관성ClearError가 isProcessingError만 초기화합니다. orderDetailError도 함께 초기화하거나 별도 이벤트로 분리해 명확히 해주세요.
Also applies to: 149-151
91-92: 상태 전체 덤프 로깅상태 전체를 로그로 남기면 데이터 노출/성능 이슈가 있습니다. 태그드/요약 로그 또는 디버그 빌드에서만 출력으로 제한해주세요.
Also applies to: 118-119, 145-146
app/src/main/java/com/sampoom/android/feature/order/ui/OrderDetailContent.kt (1)
34-64: LazyColumn 내부의 forEach 사용을 items()로 개선하는 것을 권장합니다.현재 forEach를 사용하여 item을 추가하고 있는데, 이는 LazyColumn의 최적화 기능(list diffing, key-based recomposition)을 활용하지 못합니다. 성능 개선을 위해 items() 함수 사용을 고려해보세요.
예시:
LazyColumn(...) { items(order) { singleOrder -> OrderInfoCard(order = singleOrder) } // 나머지도 유사하게 변경 }app/src/main/java/com/sampoom/android/feature/order/ui/OrderListUiEvent.kt (1)
4-5:data object사용을 권장합니다.Kotlin 1.7.20 이상에서는 싱글톤 이벤트에
object대신data object를 사용하는 것이 권장됩니다. 더 나은toString()구현과 일관성을 제공합니다.다음과 같이 수정하세요:
sealed interface OrderListUiEvent { - object LoadOrderList : OrderListUiEvent - object RetryOrderList : OrderListUiEvent + data object LoadOrderList : OrderListUiEvent + data object RetryOrderList : OrderListUiEvent }app/src/main/java/com/sampoom/android/feature/order/domain/model/OrderList.kt (1)
3-11: 중복된 computed 속성을 제거하는 것을 고려하세요.
totalCount와isEmpty는items.size와items.isEmpty()로 직접 계산할 수 있는 값입니다. 이러한 저장된 속성은 메모리 오버헤드를 추가하면서도 실질적인 이점이 없습니다.다음과 같이 리팩토링을 고려하세요:
data class OrderList( - val items: List<Order>, - val totalCount: Int = items.size, - val isEmpty: Boolean = items.isEmpty() + val items: List<Order> ) { + val totalCount: Int get() = items.size + val isEmpty: Boolean get() = items.isEmpty() + companion object Companion { fun empty() = OrderList(emptyList()) } }또는 호출부에서
items.size와items.isEmpty()를 직접 사용하도록 하여 이러한 래퍼 속성을 완전히 제거할 수도 있습니다.app/src/main/java/com/sampoom/android/feature/order/ui/OrderResultBottomSheet.kt (2)
63-69: 조건부 로직 개선 필요
clearSuccess()와LoadOrder가isProcessingCancelSuccess의 상태와 무관하게 항상 호출됩니다. 이는 불필요한 API 호출을 발생시킬 수 있습니다.다음과 같이 수정하는 것을 권장합니다:
LaunchedEffect(uiState.isProcessingCancelSuccess) { if (uiState.isProcessingCancelSuccess) { Toast.makeText(context, context.getString(R.string.order_detail_toast_order_cancel), Toast.LENGTH_SHORT).show() + viewModel.clearSuccess() + viewModel.onEvent(OrderDetailUiEvent.LoadOrder) } - viewModel.clearSuccess() - viewModel.onEvent(OrderDetailUiEvent.LoadOrder) }
46-50: API 명확성 개선 고려함수가
List<Order>를 받지만 실제로는 첫 번째 주문만 사용합니다. 단일 주문만 처리하는 경우order: Order파라미터로 변경하거나, 여러 주문을 처리하는 기능을 추가하는 것을 고려하세요.단일 주문만 처리하는 경우:
-fun OrderResultBottomSheet( - order: List<Order>, - onDismiss: () -> Unit, - viewModel: OrderDetailViewModel = hiltViewModel() -) { +fun OrderResultBottomSheet( + order: Order, + onDismiss: () -> Unit, + viewModel: OrderDetailViewModel = hiltViewModel() +) {그리고 line 58-60을 다음과 같이 수정:
- LaunchedEffect(order) { - if (order.isNotEmpty()) viewModel.setOrderIdFromApi(order.first().orderId) - } + LaunchedEffect(order.orderId) { + viewModel.setOrderIdFromApi(order.orderId) + }app/src/main/java/com/sampoom/android/feature/order/ui/OrderListScreen.kt (1)
50-53: LaunchedEffect 의존성 검토
LaunchedEffect(errorLabel)은errorLabel이 변경될 때마다 트리거되어 주문 목록을 다시 로드합니다. 일반적으로 에러 라벨은 변경되지 않으므로 문제가 없지만,LoadOrderList호출은init블록에서 이미 수행되므로 중복일 수 있습니다.다음과 같이 단순화할 수 있습니다:
- LaunchedEffect(errorLabel) { + LaunchedEffect(Unit) { viewModel.bindLabel(errorLabel) - viewModel.onEvent(OrderListUiEvent.LoadOrderList) }(
init블록에서 이미loadOrderList()가 호출되므로)app/src/main/java/com/sampoom/android/feature/order/domain/model/Order.kt (2)
6-6: 날짜 타입 개선을 고려하세요.
createdAt이String으로 정의되어 있어 날짜 비교, 포맷팅, 파싱 시 일관성 문제가 발생할 수 있습니다.LocalDateTime,Instant, 또는Long(타임스탬프) 같은 적절한 날짜 타입 사용을 권장합니다.
28-28: 수량 타입을Int로 변경하는 것을 고려하세요.
quantity가Long으로 정의되어 있지만, 일반적인 주문 수량은Int범위(최대 ~21억)를 넘지 않습니다.Int를 사용하면 메모리 효율성이 높아지고 불필요한 타입 변환을 줄일 수 있습니다.app/src/main/java/com/sampoom/android/feature/order/data/repository/OrderRepositoryImpl.kt (2)
24-29: 에러 처리를 개선하세요.
dto.message만으로Exception을 던지면 원본 에러의 맥락(에러 코드, 스택 트레이스 등)이 손실됩니다. 커스텀 예외 타입을 정의하거나 최소한 에러 코드를 포함하는 것을 권장합니다.예시:
if (!dto.success) { throw OrderProcessingException( message = dto.message ?: "Unknown error", code = dto.errorCode ) }
31-35: 메서드 시맨틱과 반환 타입의 불일치를 해결하세요.
getOrderDetail이OrderList를 반환하고 있는데, "상세 조회"는 일반적으로 단일 엔티티를 반환합니다. 백엔드가 실제로 리스트를 반환한다면 메서드명을 변경하거나, 단일 Order를 반환하도록 수정하는 것을 고려하세요.app/src/main/java/com/sampoom/android/feature/order/ui/OrderDetailScreen.kt (1)
92-109: 작업 진행 중 상태를 표시하세요.사용자가 취소 또는 입고 처리 버튼을 클릭한 후 작업이 완료될 때까지 로딩 인디케이터나 버튼 비활성화 같은 피드백이 없습니다. 이로 인해 사용자가 버튼을 여러 번 클릭할 수 있습니다.
uiState에isProcessingCancel또는isProcessingReceive같은 로딩 플래그를 추가하고, 이를 기반으로 버튼을 비활성화하는 것을 권장합니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (40)
app/src/main/java/com/sampoom/android/app/navigation/AppNavHost.kt(4 hunks)app/src/main/java/com/sampoom/android/core/network/ErrorHandling.kt(2 hunks)app/src/main/java/com/sampoom/android/core/ui/component/StatusChip.kt(1 hunks)app/src/main/java/com/sampoom/android/core/util/FormatDate.kt(1 hunks)app/src/main/java/com/sampoom/android/core/util/OrderTitle.kt(1 hunks)app/src/main/java/com/sampoom/android/feature/cart/data/repository/CartRepositoryImpl.kt(2 hunks)app/src/main/java/com/sampoom/android/feature/cart/ui/CartListScreen.kt(2 hunks)app/src/main/java/com/sampoom/android/feature/cart/ui/CartListUiEvent.kt(1 hunks)app/src/main/java/com/sampoom/android/feature/cart/ui/CartListUiState.kt(2 hunks)app/src/main/java/com/sampoom/android/feature/cart/ui/CartListViewModel.kt(4 hunks)app/src/main/java/com/sampoom/android/feature/order/data/mapper/OrderMappers.kt(1 hunks)app/src/main/java/com/sampoom/android/feature/order/data/remote/api/OrderApi.kt(1 hunks)app/src/main/java/com/sampoom/android/feature/order/data/remote/dto/OrderDto.kt(1 hunks)app/src/main/java/com/sampoom/android/feature/order/data/repository/OrderRepositoryImpl.kt(1 hunks)app/src/main/java/com/sampoom/android/feature/order/di/OrderModules.kt(1 hunks)app/src/main/java/com/sampoom/android/feature/order/domain/model/Order.kt(1 hunks)app/src/main/java/com/sampoom/android/feature/order/domain/model/OrderList.kt(1 hunks)app/src/main/java/com/sampoom/android/feature/order/domain/model/OrderStatus.kt(1 hunks)app/src/main/java/com/sampoom/android/feature/order/domain/repository/OrderRepository.kt(1 hunks)app/src/main/java/com/sampoom/android/feature/order/domain/usecase/CancelOrderUseCase.kt(1 hunks)app/src/main/java/com/sampoom/android/feature/order/domain/usecase/CreateOrderUseCase.kt(1 hunks)app/src/main/java/com/sampoom/android/feature/order/domain/usecase/GetOrderDetailUseCase.kt(1 hunks)app/src/main/java/com/sampoom/android/feature/order/domain/usecase/GetOrderUseCase.kt(1 hunks)app/src/main/java/com/sampoom/android/feature/order/domain/usecase/ReceiveOrderUseCase.kt(1 hunks)app/src/main/java/com/sampoom/android/feature/order/ui/OrderDetailContent.kt(1 hunks)app/src/main/java/com/sampoom/android/feature/order/ui/OrderDetailScreen.kt(1 hunks)app/src/main/java/com/sampoom/android/feature/order/ui/OrderDetailUiEvent.kt(1 hunks)app/src/main/java/com/sampoom/android/feature/order/ui/OrderDetailUiState.kt(1 hunks)app/src/main/java/com/sampoom/android/feature/order/ui/OrderDetailViewModel.kt(1 hunks)app/src/main/java/com/sampoom/android/feature/order/ui/OrderListScreen.kt(1 hunks)app/src/main/java/com/sampoom/android/feature/order/ui/OrderListUiEvent.kt(1 hunks)app/src/main/java/com/sampoom/android/feature/order/ui/OrderListUiState.kt(1 hunks)app/src/main/java/com/sampoom/android/feature/order/ui/OrderListViewModel.kt(1 hunks)app/src/main/java/com/sampoom/android/feature/order/ui/OrderResultBottomSheet.kt(1 hunks)app/src/main/java/com/sampoom/android/feature/outbound/data/repository/OutboundRepositoryImpl.kt(3 hunks)app/src/main/java/com/sampoom/android/feature/part/ui/PartDetailBottomSheet.kt(0 hunks)app/src/main/java/com/sampoom/android/feature/part/ui/PartListScreen.kt(0 hunks)app/src/main/java/com/sampoom/android/feature/part/ui/PartScreen.kt(1 hunks)app/src/main/res/drawable/ic_check_circle.xml(1 hunks)app/src/main/res/values/strings.xml(2 hunks)
💤 Files with no reviewable changes (2)
- app/src/main/java/com/sampoom/android/feature/part/ui/PartListScreen.kt
- app/src/main/java/com/sampoom/android/feature/part/ui/PartDetailBottomSheet.kt
🧰 Additional context used
🧬 Code graph analysis (8)
app/src/main/java/com/sampoom/android/feature/cart/ui/CartListScreen.kt (1)
app/src/main/java/com/sampoom/android/feature/order/ui/OrderResultBottomSheet.kt (1)
OrderResultBottomSheet(44-141)
app/src/main/java/com/sampoom/android/feature/part/ui/PartScreen.kt (1)
app/src/main/java/com/sampoom/android/core/ui/component/EmptyContent.kt (1)
EmptyContent(9-20)
app/src/main/java/com/sampoom/android/feature/order/ui/OrderDetailContent.kt (2)
app/src/main/java/com/sampoom/android/core/ui/theme/Color.kt (3)
textColor(256-257)backgroundCardColor(253-254)textSecondaryColor(259-260)app/src/main/java/com/sampoom/android/core/ui/component/StatusChip.kt (1)
StatusChip(18-37)
app/src/main/java/com/sampoom/android/feature/order/ui/OrderResultBottomSheet.kt (3)
app/src/main/java/com/sampoom/android/feature/order/ui/OrderDetailContent.kt (1)
OrderDetailContent(29-65)app/src/main/java/com/sampoom/android/core/ui/component/CommonButton.kt (1)
CommonButton(49-176)app/src/main/java/com/sampoom/android/core/ui/theme/Color.kt (1)
textColor(256-257)
app/src/main/java/com/sampoom/android/feature/order/ui/OrderDetailScreen.kt (4)
app/src/main/java/com/sampoom/android/core/ui/component/CommonButton.kt (1)
CommonButton(49-176)app/src/main/java/com/sampoom/android/core/ui/component/ErrorContent.kt (1)
ErrorContent(18-37)app/src/main/java/com/sampoom/android/core/ui/component/EmptyContent.kt (1)
EmptyContent(9-20)app/src/main/java/com/sampoom/android/feature/order/ui/OrderDetailContent.kt (1)
OrderDetailContent(29-65)
app/src/main/java/com/sampoom/android/feature/order/ui/OrderListScreen.kt (6)
app/src/main/java/com/sampoom/android/core/ui/theme/Color.kt (3)
textColor(256-257)backgroundCardColor(253-254)textSecondaryColor(259-260)app/src/main/java/com/sampoom/android/core/ui/component/ErrorContent.kt (1)
ErrorContent(18-37)app/src/main/java/com/sampoom/android/core/ui/component/EmptyContent.kt (1)
EmptyContent(9-20)app/src/main/java/com/sampoom/android/core/util/OrderTitle.kt (1)
buildOrderTitle(6-26)app/src/main/java/com/sampoom/android/core/util/FormatDate.kt (1)
formatDate(3-11)app/src/main/java/com/sampoom/android/core/ui/component/StatusChip.kt (1)
StatusChip(18-37)
app/src/main/java/com/sampoom/android/feature/order/ui/OrderDetailViewModel.kt (3)
app/src/main/java/com/sampoom/android/feature/order/data/repository/OrderRepositoryImpl.kt (2)
cancelOrder(37-42)receiveOrder(24-29)app/src/main/java/com/sampoom/android/feature/order/data/remote/api/OrderApi.kt (2)
cancelOrder(30-31)receiveOrder(22-23)app/src/main/java/com/sampoom/android/feature/order/domain/repository/OrderRepository.kt (2)
cancelOrder(10-10)receiveOrder(8-8)
app/src/main/java/com/sampoom/android/app/navigation/AppNavHost.kt (2)
app/src/main/java/com/sampoom/android/feature/order/ui/OrderDetailScreen.kt (1)
OrderDetailScreen(40-194)app/src/main/java/com/sampoom/android/feature/order/ui/OrderListScreen.kt (1)
OrderListScreen(41-130)
🔇 Additional comments (26)
app/src/main/java/com/sampoom/android/feature/part/ui/PartScreen.kt (1)
119-119: 변경사항은 주문 관리 PR의 의도된 일부입니다검증 결과, git 커밋 d54ad66 "주문관리 화면, 주문관리 상세 화면 구현"에서 PartScreen.kt이 수정되었으므로, 이번 PR에 의도적으로 포함된 변경사항입니다.
해당 라인들의 변경사항(ErrorContent와 EmptyContent에 높이와 너비 수정자 추가)은 에러 및 빈 상태의 UI를 개선하는 정당한 수정입니다. 우려사항은 해결되었습니다.
app/src/main/java/com/sampoom/android/feature/order/ui/OrderDetailUiEvent.kt (1)
3-9: 이벤트 모델 깔끔합니다단일 책임, 명료한 분리. 현재 요구사항에 적합해 보입니다.
app/src/main/java/com/sampoom/android/feature/order/data/remote/dto/OrderDto.kt (1)
5-31: DTO 구조가 깔끔하게 설계되었습니다.데이터 계층의 DTO가 도메인 모델(OrderStatus)을 적절히 참조하고 있으며, nullable 필드 처리도 올바르게 되어 있습니다.
app/src/main/java/com/sampoom/android/feature/cart/ui/CartListUiEvent.kt (1)
12-12: 새로운 이벤트 추가가 적절합니다.기존 sealed interface 패턴을 따르고 있으며, 주문 결과 UI 해제를 위한 명확한 의도를 가지고 있습니다.
app/src/main/java/com/sampoom/android/feature/order/domain/usecase/GetOrderUseCase.kt (1)
6-10: Use case 구현이 올바릅니다.클린 아키텍처 패턴을 따르고 있으며, repository로의 위임이 적절합니다.
app/src/main/java/com/sampoom/android/feature/cart/ui/CartListScreen.kt (1)
63-68: 주문 결과 UI 개선이 잘 되었습니다.Toast 기반에서 BottomSheet로 변경하여 사용자 경험이 향상되었으며, nullable 처리와 이벤트 핸들링이 적절합니다.
app/src/main/java/com/sampoom/android/feature/order/domain/usecase/CancelOrderUseCase.kt (1)
6-10: 주문 취소 use case가 적절히 구현되었습니다.orderId를 받아 repository로 위임하는 구조가 명확하며, 다른 use case들과 일관된 패턴을 따릅니다.
app/src/main/java/com/sampoom/android/core/ui/component/StatusChip.kt (1)
19-36: 상태 칩 컴포넌트가 잘 구현되었습니다.모든 OrderStatus 케이스를 처리하고 있으며, 색상 선택이 의미론적으로 적절합니다. 재사용 가능한 컴포넌트로 설계되어 있습니다.
app/src/main/java/com/sampoom/android/feature/order/domain/usecase/CreateOrderUseCase.kt (1)
6-10: 주문 생성 use case가 올바르게 구현되었습니다.장바구니 기반 주문 생성 플로우에 적합한 구조이며, repository로의 위임이 깔끔합니다.
app/src/main/java/com/sampoom/android/feature/order/ui/OrderDetailContent.kt (1)
69-188: 헬퍼 컴포저블들이 잘 설계되었습니다.카드 스타일링이 일관되고, null 처리가 적절하며, 테마 색상 통합이 올바르게 되어 있습니다.
app/src/main/java/com/sampoom/android/app/navigation/AppNavHost.kt (1)
121-133: 주문 상세 라우팅 구현이 올바릅니다.네비게이션 인자 설정과 화면 연결이 적절하게 구현되어 있습니다.
app/src/main/java/com/sampoom/android/feature/order/domain/usecase/GetOrderDetailUseCase.kt (1)
6-10: 깔끔한 Use Case 구현입니다.리포지토리 위임 패턴이 올바르게 적용되었으며, Clean Architecture 원칙을 잘 따르고 있습니다.
app/src/main/java/com/sampoom/android/feature/order/ui/OrderListUiState.kt (1)
5-9: 잘 구조화된 UI 상태입니다.로딩, 데이터, 에러 상태를 명확하게 표현하는 표준적인 패턴이며, 기본값도 적절합니다.
app/src/main/java/com/sampoom/android/feature/order/di/OrderModules.kt (2)
14-19: Repository 바인딩이 올바르게 구현되었습니다.Hilt의
@Binds패턴을 적절히 사용하여 인터페이스와 구현체를 연결하고 있습니다.
21-26: API 제공이 올바르게 구현되었습니다.Retrofit 인스턴스로부터 API를 생성하는 표준적인 패턴입니다.
app/src/main/res/values/strings.xml (2)
81-100: 주문 관리 문자열 리소스가 잘 정의되었습니다.주문 관리 화면에 필요한 모든 문자열이 명확하고 일관되게 정의되어 있습니다.
111-111: 공통 구분자 문자열이 추가되었습니다.날짜나 기타 형식에 사용될 구분자가 적절히 추가되었습니다.
app/src/main/java/com/sampoom/android/feature/cart/ui/CartListUiState.kt (1)
15-17: 더 세밀한 상태 관리로 개선되었습니다.단순한
isOrderSuccess불린 값을isProcessing,processError,processedOrder로 분리하여 주문 처리 흐름을 더 명확하게 표현하고 있습니다. 이는 UI에서 다양한 상태를 더 잘 처리할 수 있게 합니다.app/src/main/java/com/sampoom/android/feature/cart/ui/CartListViewModel.kt (1)
87-107: 주문 처리 로직이 잘 구현되었습니다
CreateOrderUseCase를 사용한 주문 생성 흐름이 올바르게 구현되었으며, 성공/실패 시 상태 관리가 적절합니다. 주문 생성 후 장바구니 목록을 다시 로드하는 것도 좋은 접근입니다.app/src/main/java/com/sampoom/android/feature/order/domain/repository/OrderRepository.kt (1)
6-10: 반환 타입 명확성 검토 필요일부 메서드의 반환 타입이 메서드 이름과 일치하지 않습니다:
getOrderDetail(orderId: Long): OrderList- 단일 주문의 상세 정보를 조회하는데OrderList를 반환합니다.createOrder(): OrderList- 주문 생성 시 여러 주문이 생성되는지 명확하지 않습니다.백엔드 API 응답 구조에 따라 결정되겠지만, 메서드 이름과 반환 타입의 의미적 일관성을 확인하시기 바랍니다.
app/src/main/java/com/sampoom/android/feature/order/ui/OrderListViewModel.kt (1)
44-69: 주문 목록 로딩 로직이 잘 구현되었습니다
GetOrderUseCase를 사용한 주문 목록 로딩 로직이 올바르게 구현되었으며, 에러 처리와 상태 관리가 프로젝트의 다른 ViewModel들과 일관된 패턴을 따릅니다.app/src/main/java/com/sampoom/android/feature/order/domain/usecase/ReceiveOrderUseCase.kt (1)
6-10: UseCase 구현이 적절합니다Repository를 래핑하는 간단한 UseCase로 프로젝트의 클린 아키텍처 패턴을 따르고 있습니다. 향후 비즈니스 로직 추가가 필요한 경우 이 레이어에서 처리할 수 있습니다.
app/src/main/java/com/sampoom/android/feature/order/data/mapper/OrderMappers.kt (1)
12-15: 매퍼 구현이 간결하고 명확합니다DTO를 도메인 모델로 변환하는 확장 함수들이 잘 구현되었습니다. 재귀적인 매핑도 올바르게 처리되고 있습니다.
가독성을 더 높이고 싶다면 named parameter를 사용하는 것도 고려할 수 있습니다:
fun OrderDto.toModel(): Order = Order( orderId = orderId, orderNumber = orderNumber, createdAt = createdAt, status = status, agencyName = agencyName, items = items.map { it.toModel() } )하지만 현재 코드도 충분히 명확하므로 선택사항입니다.
app/src/main/java/com/sampoom/android/feature/order/data/remote/api/OrderApi.kt (2)
18-19: 주문 생성 API 설계를 검증하세요.POST 요청인데 요청 바디가 없고 응답으로
List<OrderDto>를 반환하는 것이 의도된 설계인지 확인이 필요합니다. 일반적으로:
- 장바구니 항목을 바탕으로 주문을 생성할 때 요청 바디에 주문 데이터가 필요합니다
- 생성된 단일 주문을 반환하는 것이 일반적입니다
백엔드 API 명세와 일치하는지 확인해주세요.
26-27: 주문 상세 조회 응답 타입을 검증하세요.
getOrderDetail이List<OrderDto>를 반환하고 있는데, "상세 조회"는 일반적으로 단일 객체를 반환합니다. 백엔드가 실제로 리스트를 반환하는 것이 맞는지 확인이 필요하며, 맞다면 메서드명을getOrderDetails로 변경하는 것을 고려하세요.app/src/main/java/com/sampoom/android/feature/order/data/repository/OrderRepositoryImpl.kt (1)
12-16: null 안전성 문제는 없습니다. 코드 타입 분석 결과:
ApiResponse<T>의data필드는 비-nullable 타입 (val data: T)으로 선언됨OrderApi.getOrderList()의 반환 타입:ApiResponse<List<OrderDto>>(List는 비-nullable)- 따라서
dto.data는List<OrderDto>타입이며 null 불가능- Kotlin의 타입 시스템이 컴파일 시점에 null 안전성을 보장함
코드는 이미 type-safe하므로 NPE 위험이 없습니다.
Likely an incorrect or invalid review comment.
There was a problem hiding this comment.
Actionable comments posted: 9
♻️ Duplicate comments (1)
app/src/main/java/com/sampoom/android/app/navigation/AppNavHost.kt (1)
155-162: agencyId 하드코딩 재발 — 실제 사용자 컨텍스트 사용으로 교체하세요이전 리뷰에서 지적된 동일 이슈입니다.
routeOrderDetail(1, order.orderId)는 실제agencyId를 사용해야 합니다.- parentNavController.navigate(routeOrderDetail(1, order.orderId)) + val agencyId = userSession.currentAgencyId + parentNavController.navigate(routeOrderDetail(agencyId, order.orderId))
🧹 Nitpick comments (15)
app/src/main/java/com/sampoom/android/feature/part/ui/PartViewModel.kt (1)
93-115: 예외 처리가 올바르게 구현되었으며, 경쟁 조건 방지 로직이 적절합니다.구현이 잘 되어있습니다:
- Line 96과 108의 stale request 체크가 카테고리 빠른 전환 시 경쟁 조건을 방지합니다
- CancellationException 처리가 올바릅니다 (lines 104-106)
- 에러 경로에서도 stale 체크를 수행하여 일관성을 유지합니다
loadCategory()와loadGroup()간 에러 처리 패턴이 중복됩니다. 선택적으로 공통 헬퍼 함수 추출을 고려할 수 있습니다:private suspend fun <T> executeWithErrorHandling( updateLoading: (Boolean) -> Unit, updateError: (String?) -> Unit, block: suspend () -> T ): Result<T> { updateLoading(true) return try { val result = block() updateError(null) Result.success(result) } catch (ce: CancellationException) { throw ce } catch (throwable: Throwable) { val backendMessage = throwable.serverMessageOrNull() updateError(backendMessage ?: (throwable.message ?: errorLabel)) Result.failure(throwable) } finally { updateLoading(false) } }하지만 현재 구현도 명확하고 읽기 쉬우므로 필수 변경사항은 아닙니다.
app/src/main/java/com/sampoom/android/feature/part/ui/PartListViewModel.kt (1)
78-78: 로그 문 위치를 개선하는 것을 고려해보세요.현재 로그 문은 성공과 실패 케이스 모두에서 실행됩니다. 의도적인 것이라면 문제없지만, 다음 중 하나를 고려해보세요:
- 성공 케이스 내부로 이동하여 성공 시에만 로깅
finally블록으로 이동하여 의도를 명확히 표현- 성공/실패에 따라 다른 로그 레벨 사용
성공 케이스 내부로 이동하는 예시:
try { val partList = getPartListUseCase(groupId) _uiState.update { it.copy( partList = partList.items, partListLoading = false, partListError = null ) } + Log.d(TAG, "submit: ${_uiState.value}") } catch (ce: CancellationException) { throw ce } catch (throwable: Throwable) { val backendMessage = throwable.serverMessageOrNull() _uiState.update { it.copy( partListLoading = false, partListError = backendMessage ?: (throwable.message ?: errorLabel) ) } } - Log.d(TAG, "submit: ${_uiState.value}") } }app/src/main/java/com/sampoom/android/feature/outbound/data/repository/OutboundRepositoryImpl.kt (2)
10-10: CancellationException 처리가 올바르게 개선되었습니다.
runCatching에서 명시적인 try/catch 블록으로 전환하여 코루틴 취소가 올바르게 전파됩니다. 이전 리뷰 피드백을 잘 반영했습니다.다만, 이전 리뷰에서 제안한 도메인 예외 사용과 null-safe 메시지 처리는 아직 적용되지 않았습니다.
dto.message가 null일 수 있으므로 다음과 같은 개선을 고려해주세요:-if (!dto.success) throw Exception(dto.message) +if (!dto.success) throw Exception(dto.message ?: "Operation failed")또는 프로젝트 전체에서 사용할 도메인 예외 클래스를 정의하는 것을 권장합니다.
Also applies to: 21-31, 37-45, 49-57, 61-69, 76-84
26-26: 코드 스타일: catch 블록의 콜론 앞뒤 공백 불일치
catch (ce : CancellationException)패턴에서 콜론 앞뒤에 공백이 있습니다. Kotlin 컨벤션에 따르면 콜론 앞에는 공백이 없고 뒤에만 있어야 합니다.-} catch (ce : CancellationException) { +} catch (ce: CancellationException) {동일한 파일의
catch (t : Throwable)부분도 동일하게 수정이 필요합니다.Also applies to: 41-41, 53-53, 65-65, 80-80
app/src/main/java/com/sampoom/android/feature/cart/data/repository/CartRepositoryImpl.kt (3)
9-9: CancellationException import가 OutboundRepositoryImpl과 다릅니다.이 파일은
kotlinx.coroutines.CancellationException을 import하지만,OutboundRepositoryImpl.kt는kotlin.coroutines.cancellation.CancellationException을 import합니다.두 import는 기능적으로 동일하지만 (kotlinx.coroutines 버전은 typealias), 일관성을 위해 동일한 import를 사용하는 것을 권장합니다. Kotlin 1.4+ 표준 라이브러리의
kotlin.coroutines.cancellation.CancellationException을 사용하는 것이 선호됩니다.
26-34: CancellationException 처리가 올바르게 구현되었습니다.
runCatching에서 명시적인 try/catch로 전환하여 코루틴 취소 시맨틱이 올바르게 유지됩니다.하지만 OutboundRepositoryImpl과 마찬가지로,
dto.message가 null일 수 있으므로 null-safe 처리를 추가하는 것을 권장합니다:-if (!dto.success) throw Exception(dto.message) +if (!dto.success) throw Exception(dto.message ?: "Cart operation failed")Also applies to: 38-46, 50-58, 65-73
32-32: 코드 스타일: Throwable catch 블록의 콜론 공백
catch (t : Throwable)선언에서 콜론 앞뒤의 공백이 Kotlin 컨벤션과 일치하지 않습니다. 콜론 앞에는 공백이 없어야 합니다.-} catch (t : Throwable) { +} catch (t: Throwable) {참고: 동일한 파일의
catch (ce: CancellationException)부분은 올바르게 작성되어 있습니다.Also applies to: 44-44, 56-56, 71-71
app/src/main/java/com/sampoom/android/core/util/FormatDate.kt (2)
11-13: 오프셋 감지 휴리스틱이 취약합니다 — 파싱 시도 우선 흐름으로 리팩터 권장
contains('-').and(colonCount>=3)방식은-0300,+09등 유효한 형식을 놓칠 수 있고, 반대로 우연한 문자열을 오인식할 수 있습니다. 오프셋 유무를 “추측”하지 말고 “파싱을 먼저 시도”하세요.권장 리팩터(개요):
- 1순위:
ISO_OFFSET_DATE_TIME로 파싱 → 기기 타임존 변환 → LocalDate- 실패 시:
yyyy-MM-dd'T'HH:mm:ss[.fraction(0..6)]- 그래도 실패 시:
ISO_LOCAL_DATE예시:
- val hasOffset = - dateString.endsWith("Z") || dateString.contains('+') || dateString.contains('-') - .and(dateString.count { it == ':' } >= 3) - val date = if (hasOffset) { - val inFmt = java.time.format.DateTimeFormatter.ISO_OFFSET_DATE_TIME - java.time.OffsetDateTime.parse(dateString, inFmt).toLocalDate() - } else { - val inFmt = java.time.format.DateTimeFormatterBuilder() - .appendPattern("yyyy-MM-dd'T'HH:mm:ss") - .optionalStart() - .appendFraction(java.time.temporal.ChronoField.NANO_OF_SECOND, 0, 6, true) - .optionalEnd() - .toFormatter(java.util.Locale.ROOT) - java.time.LocalDateTime.parse(dateString, inFmt).toLocalDate() - } + val date = runCatching { + java.time.OffsetDateTime.parse( + dateString, + java.time.format.DateTimeFormatter.ISO_OFFSET_DATE_TIME + ).atZoneSameInstant(java.time.ZoneId.systemDefault()).toLocalDate() + }.recoverCatching { + val inFmt = java.time.format.DateTimeFormatterBuilder() + .appendPattern("yyyy-MM-dd'T'HH:mm:ss") + .optionalStart() + .appendFraction(java.time.temporal.ChronoField.NANO_OF_SECOND, 0, 6, true) + .optionalEnd() + .toFormatter(java.util.Locale.ROOT) + java.time.LocalDateTime.parse(dateString, inFmt).toLocalDate() + }.recoverCatching { + java.time.LocalDate.parse( + dateString, + java.time.format.DateTimeFormatter.ISO_LOCAL_DATE + ) + }.getOrThrow()
18-24: Formatter 매번 생성 비용을 줄이기 위해 캐싱 권장
DateTimeFormatterBuilder결과를 매 호출마다 생성하지 말고 파일 상수로 재사용하면 가비지와 비용을 줄일 수 있습니다.추가 코드(파일 상단에 배치):
private val FMT_LOCAL_DATE: java.time.format.DateTimeFormatter = java.time.format.DateTimeFormatter.ISO_LOCAL_DATE private val FMT_LOCAL_DATE_TIME_OPT_6: java.time.format.DateTimeFormatter = java.time.format.DateTimeFormatterBuilder() .appendPattern("yyyy-MM-dd'T'HH:mm:ss") .optionalStart() .appendFraction(java.time.temporal.ChronoField.NANO_OF_SECOND, 0, 6, true) .optionalEnd() .toFormatter(java.util.Locale.ROOT)그리고 본문에서는
FMT_LOCAL_DATE_TIME_OPT_6/FMT_LOCAL_DATE를 참조하세요.app/src/main/java/com/sampoom/android/app/navigation/AppNavHost.kt (1)
46-48: 주문 상세 라우트 경로가orders/.../orders/...로 중복되어 비일관적입니다리소스 계층을 명확히 하도록 경로를 정리하세요. 권장:
- 상위 리소스(agency) → 하위 리소스(orders)
다음과 같이 변경 제안:
-const val ROUTE_ORDER_DETAIL = "orders/{agencyId}/orders/{orderId}" -fun routeOrderDetail(agencyId: Long, orderId: Long): String = "orders/$agencyId/orders/$orderId" +const val ROUTE_ORDER_DETAIL = "agencies/{agencyId}/orders/{orderId}" +fun routeOrderDetail(agencyId: Long, orderId: Long): String = "agencies/$agencyId/orders/$orderId"ROUTE_ORDER_DETAIL를 참조하는 네비게이션 그래프와 딥링크가 있다면 함께 업데이트 필요합니다.
app/src/main/java/com/sampoom/android/feature/order/ui/OrderListScreen.kt (2)
119-125: LazyColumn에 stable key 지정으로 스크롤/애니메이션 안정성 확보아이템에 고유 ID가 있으므로 key를 지정하세요.
- items(uiState.orderList) { order -> + items(uiState.orderList, key = { it.orderId }) { order -> OrderItem( order = order, onClick = { onNavigateOrderDetail(order) } ) }
15-16: list state 저장 방식 단순화 (선택)
rememberLazyListState()는 기본적으로 저장 가능하며 코드가 간결합니다.-import androidx.compose.foundation.lazy.LazyListState +import androidx.compose.foundation.lazy.rememberLazyListState ... - val listState = rememberSaveable(saver = LazyListState.Saver) { LazyListState() } + val listState = rememberLazyListState()Also applies to: 50-50
app/src/main/java/com/sampoom/android/feature/order/ui/OrderListViewModel.kt (1)
69-69: 로그는 Timber 등 일관 로깅으로 통일 권장
Log.d대신 프로젝트 표준 로거 사용을 권장합니다.- Log.d(TAG, "submit: ${_uiState.value}") + Timber.d("OrderList state: %s", _uiState.value)(해당 라이브러리를 이미 사용 중인 경우에 한함)
app/src/main/res/values/strings.xml (2)
81-96: 용어/문구 통일성 개선 제안 (선택)한국어 자연스러움과 UI 일관성을 위해 다음을 권장합니다.
- <string name="order_title">주문관리</string> - <string name="order_empty_list">주문관리 목록이 없습니다.</string> - <string name="order_detail_title">주문정보</string> - <string name="order_detail_dialog_order_cancel">주문 취소처리하시겠습니까?</string> - <string name="order_detail_toast_order_cancel">주문 취소처리되었습니다</string> - <string name="order_detail_order_receive">입고처리</string> - <string name="order_detail_dialog_order_receive">입고 처리하시겠습니까?</string> - <string name="order_detail_toast_order_receive">입고 처리되었습니다</string> + <string name="order_title">주문 관리</string> + <string name="order_empty_list">주문 목록이 없습니다.</string> + <string name="order_detail_title">주문 정보</string> + <string name="order_detail_dialog_order_cancel">주문을 취소하시겠습니까?</string> + <string name="order_detail_toast_order_cancel">주문이 취소되었습니다</string> + <string name="order_detail_order_receive">입고 처리</string> + <string name="order_detail_dialog_order_receive">입고를 처리하시겠습니까?</string> + <string name="order_detail_toast_order_receive">입고가 처리되었습니다</string>
111-111: 리소스 키와 값 불일치
common_slash인데 값이-(대시)입니다. 혼동을 줄이려면 값 또는 키를 일치시키세요.- <string name="common_slash">-</string> + <string name="common_slash">/</string>(이미 여러 곳에서 대시를 의도했다면 키를
common_dash로 새로 추가 후 점진 교체를 검토하세요.)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (24)
app/src/main/java/com/sampoom/android/app/navigation/AppNavHost.kt(5 hunks)app/src/main/java/com/sampoom/android/core/util/FormatDate.kt(1 hunks)app/src/main/java/com/sampoom/android/core/util/OrderTitle.kt(1 hunks)app/src/main/java/com/sampoom/android/feature/cart/data/remote/api/CartApi.kt(1 hunks)app/src/main/java/com/sampoom/android/feature/cart/data/repository/CartRepositoryImpl.kt(2 hunks)app/src/main/java/com/sampoom/android/feature/cart/ui/CartListViewModel.kt(3 hunks)app/src/main/java/com/sampoom/android/feature/order/data/remote/api/OrderApi.kt(1 hunks)app/src/main/java/com/sampoom/android/feature/order/data/repository/OrderRepositoryImpl.kt(1 hunks)app/src/main/java/com/sampoom/android/feature/order/di/OrderModules.kt(1 hunks)app/src/main/java/com/sampoom/android/feature/order/domain/model/OrderStatus.kt(1 hunks)app/src/main/java/com/sampoom/android/feature/order/ui/OrderDetailScreen.kt(1 hunks)app/src/main/java/com/sampoom/android/feature/order/ui/OrderDetailViewModel.kt(1 hunks)app/src/main/java/com/sampoom/android/feature/order/ui/OrderListScreen.kt(1 hunks)app/src/main/java/com/sampoom/android/feature/order/ui/OrderListViewModel.kt(1 hunks)app/src/main/java/com/sampoom/android/feature/order/ui/OrderResultBottomSheet.kt(1 hunks)app/src/main/java/com/sampoom/android/feature/outbound/data/remote/api/OutboundApi.kt(1 hunks)app/src/main/java/com/sampoom/android/feature/outbound/data/repository/OutboundRepositoryImpl.kt(2 hunks)app/src/main/java/com/sampoom/android/feature/outbound/ui/OutboundListScreen.kt(1 hunks)app/src/main/java/com/sampoom/android/feature/outbound/ui/OutboundListViewModel.kt(2 hunks)app/src/main/java/com/sampoom/android/feature/part/data/remote/api/PartApi.kt(1 hunks)app/src/main/java/com/sampoom/android/feature/part/ui/PartDetailBottomSheet.kt(1 hunks)app/src/main/java/com/sampoom/android/feature/part/ui/PartListViewModel.kt(2 hunks)app/src/main/java/com/sampoom/android/feature/part/ui/PartViewModel.kt(3 hunks)app/src/main/res/values/strings.xml(2 hunks)
✅ Files skipped from review due to trivial changes (2)
- app/src/main/java/com/sampoom/android/feature/outbound/data/remote/api/OutboundApi.kt
- app/src/main/java/com/sampoom/android/feature/part/data/remote/api/PartApi.kt
🚧 Files skipped from review as they are similar to previous changes (9)
- app/src/main/java/com/sampoom/android/feature/order/domain/model/OrderStatus.kt
- app/src/main/java/com/sampoom/android/feature/order/data/remote/api/OrderApi.kt
- app/src/main/java/com/sampoom/android/feature/order/di/OrderModules.kt
- app/src/main/java/com/sampoom/android/feature/order/ui/OrderDetailScreen.kt
- app/src/main/java/com/sampoom/android/feature/cart/ui/CartListViewModel.kt
- app/src/main/java/com/sampoom/android/feature/order/data/repository/OrderRepositoryImpl.kt
- app/src/main/java/com/sampoom/android/feature/part/ui/PartDetailBottomSheet.kt
- app/src/main/java/com/sampoom/android/feature/order/ui/OrderDetailViewModel.kt
- app/src/main/java/com/sampoom/android/core/util/OrderTitle.kt
🧰 Additional context used
🧬 Code graph analysis (3)
app/src/main/java/com/sampoom/android/app/navigation/AppNavHost.kt (2)
app/src/main/java/com/sampoom/android/feature/order/ui/OrderDetailScreen.kt (1)
OrderDetailScreen(41-205)app/src/main/java/com/sampoom/android/feature/order/ui/OrderListScreen.kt (1)
OrderListScreen(43-130)
app/src/main/java/com/sampoom/android/feature/order/ui/OrderListScreen.kt (5)
app/src/main/java/com/sampoom/android/core/ui/theme/Color.kt (3)
textColor(256-257)backgroundCardColor(253-254)textSecondaryColor(259-260)app/src/main/java/com/sampoom/android/core/ui/component/ErrorContent.kt (1)
ErrorContent(18-37)app/src/main/java/com/sampoom/android/core/ui/component/EmptyContent.kt (1)
EmptyContent(9-20)app/src/main/java/com/sampoom/android/core/util/OrderTitle.kt (1)
buildOrderTitle(6-26)app/src/main/java/com/sampoom/android/core/ui/component/StatusChip.kt (1)
StatusChip(18-37)
app/src/main/java/com/sampoom/android/feature/order/ui/OrderResultBottomSheet.kt (3)
app/src/main/java/com/sampoom/android/feature/order/ui/OrderDetailContent.kt (1)
OrderDetailContent(29-65)app/src/main/java/com/sampoom/android/core/ui/component/CommonButton.kt (1)
CommonButton(49-176)app/src/main/java/com/sampoom/android/core/ui/theme/Color.kt (1)
textColor(256-257)
🔇 Additional comments (17)
app/src/main/java/com/sampoom/android/feature/outbound/ui/OutboundListScreen.kt (1)
70-75: 조건부 clearSuccess() 호출로 개선되었습니다.
viewModel.clearSuccess()를if블록 내부로 이동시킨 것은 올바른 개선입니다. 이전 구현에서는uiState.isOrderSuccess가 변경될 때마다(true→false 전환 포함)clearSuccess()가 호출되어 중복 상태 업데이트가 발생했습니다. 현재 구현은 실제로 성공 상태가true일 때만 클리어하므로 불필요한 상태 업데이트를 방지합니다.app/src/main/java/com/sampoom/android/feature/part/ui/PartViewModel.kt (2)
11-11: LGTM! CancellationException import가 올바르게 추가되었습니다.명시적인 코루틴 취소 처리를 위해 필요한 import입니다.
56-75: 명시적인 예외 처리로 변경되어 코루틴 취소 의미론이 올바르게 보존됩니다.변경사항이 잘 구현되었습니다:
- CancellationException을 명시적으로 재throw하여 코루틴 취소를 보존합니다 (lines 65-66)
- 백엔드 메시지 → throwable.message → errorLabel 순서로 폴백하는 에러 메시지 체인이 적절합니다
- 상태 업데이트(loading, error, data)가 올바르게 처리됩니다
PR의 다른 ViewModel들과 일관된 패턴을 따르고 있습니다.
app/src/main/java/com/sampoom/android/feature/part/ui/PartListViewModel.kt (3)
10-10: 필요한 import 추가를 확인했습니다.아래 CancellationException 처리를 위해 필요한 import입니다.
58-66: 성공 케이스 처리가 올바르게 구현되었습니다.파트 리스트를 로드하고 UI 상태를 적절하게 업데이트합니다.
67-77: 코루틴 취소 및 에러 처리가 모범 사례를 따르고 있습니다.CancellationException을 명시적으로 재throw하여 코루틴 취소 의미를 보존하고, 백엔드 메시지 추출을 통해 사용자에게 유용한 에러 메시지를 제공합니다.
app/src/main/java/com/sampoom/android/feature/cart/data/remote/api/CartApi.kt (1)
15-15: TODO 주석으로 기술 부채를 명확히 표시했습니다.AgencyId를 동적으로 주입해야 하는 요구사항을 TODO로 표시한 것은 좋습니다. 현재 모든 엔드포인트에서 "agency/1"을 하드코딩하고 있는데, 향후 멀티 에이전시 지원이나 동적 에이전시 선택이 필요할 때 이 부분을 수정해야 합니다.
이 TODO가 추적되고 있는지 확인해주세요 (예: Jira 이슈 생성).
app/src/main/java/com/sampoom/android/feature/outbound/ui/OutboundListViewModel.kt (1)
13-13: LGTM!코루틴 취소 처리를 위한 필수 import입니다.
app/src/main/java/com/sampoom/android/feature/order/ui/OrderResultBottomSheet.kt (2)
64-70: 주문 취소 성공 후 바텀시트 닫기 동작 검증 필요주문 취소가 성공하면 토스트를 표시하고 주문을 다시 로드하지만, 바텀시트는 닫히지 않습니다. 일반적으로 사용자는 주문 취소 후 바텀시트가 자동으로 닫힐 것으로 예상할 수 있습니다.
의도된 UX가 맞는지 확인하고, 바텀시트를 닫아야 한다면 다음과 같이 수정:
LaunchedEffect(uiState.isProcessingCancelSuccess) { if (uiState.isProcessingCancelSuccess) { Toast.makeText(context, context.getString(R.string.order_detail_toast_order_cancel), Toast.LENGTH_SHORT).show() viewModel.clearSuccess() - viewModel.onEvent(OrderDetailUiEvent.LoadOrder) + onDismiss() } }
123-173: 주문 취소 다이얼로그 및 헤더 구현 양호AlertDialog의 확인/취소 흐름이 올바르게 구현되었고, OrderCompleteHeader는 적절한 스타일링으로 명확한 성공 UI를 제공합니다.
app/src/main/java/com/sampoom/android/core/util/FormatDate.kt (3)
7-10: java.time 전환과 실패 시 안전한 폴백 좋습니다
java.time사용과runCatching { … }.getOrElse { … }패턴은 안정적입니다. 👍
7-8: Core library desugaring이 미활성화되어 있음 — @RequiresApi 어노테이션은 필요함프로젝트 설정을 확인한 결과, core library desugaring이 활성화되지 않았습니다:
minSdk = 24(API 26 이하 지원 필요)coreLibraryDesugaringEnabled설정 없음desugar_jdk_libs의존성 없음
formatDate()함수는java.time.*API를 사용하며, 이는 API 26에서 도입되었습니다. 따라서 현재@RequiresApi(Build.VERSION_CODES.O)어노테이션은 적절하고 필요하므로 유지해야 합니다. 코드를 그대로 두셔도 됩니다.
15-17: 기술적 분석 정확성 확인됨 – 개발자의 의도 확인 필요리뷰 코멘트의 기술적 우려사항이 정확합니다.
OffsetDateTime.parse(dateString, inFmt).toLocalDate()는 입력 문자열의 오프셋 기준 달력 날짜만 반환하며, 기기의 타임존(예: 서울 +09:00)으로 변환하지 않습니다.조사 결과:
- 함수는
OrderListScreen.kt라인 169에서order.createdAt포맷팅 시 호출됨- 서버에서 받은 ISO 8601 오프셋 타임스탐프를 처리하는 용도
확인 필요 사항:
비즈니스 요구사항에 따라 수정이 필요합니다:
- "서버 기준 날짜"를 원하는 경우: 현재 코드 유지 (또는 명확히 문서화)
- "사용자 기기 기준 날짜"를 원하는 경우: 리뷰에서 제안한
atZoneSameInstant(ZoneId.systemDefault()).toLocalDate()적용 필수개발자가 이 화면(주문/입고 일자 표시)의 의도를 검토하여 수정 여부를 결정해 주시기 바랍니다.
app/src/main/java/com/sampoom/android/app/navigation/AppNavHost.kt (2)
26-27: 신규 화면 import 사용 확인 완료두 import 모두 아래에서 실제로 사용됩니다. 문제 없습니다.
121-133: 라우트 인자 키가 올바르게 일치합니다 — 검증 완료검증 결과, OrderDetailViewModel에서 SavedStateHandle을 통해 올바르게 인자를 읽고 있습니다:
agencyId: 라우트 정의 ↔savedStateHandle.get<Long>("agencyId")✓orderId: 라우트 정의 ↔savedStateHandle.get<Long>("orderId")✓키 이름이 정확히 일치하며, 폴백값(
?: 0L)으로 안전하게 처리되고 있어 데이터 로드 실패 위험은 없습니다.app/src/main/res/values/strings.xml (2)
76-76: 오타 수정 반영 확인텍스트가 자연스럽게 수정되었습니다. 좋습니다.
78-78: 주문 완료 토스트 문구 추가 반영 OK사용자 피드백에 적절합니다.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (2)
app/src/main/java/com/sampoom/android/feature/order/ui/OrderListScreen.kt (2)
136-136: 불필요한 람다 래핑 제거 가능
onClick = { onClick() }는 불필요한 람다 래핑입니다. 직접 참조를 전달할 수 있습니다.- onClick = { onClick() }, + onClick = onClick,
135-176: 접근성 개선 고려사항
OrderItem에 의미있는 semantics 정보를 추가하면 스크린 리더 사용자 경험이 향상됩니다. 예를 들어, 각 주문 카드에 대한 설명적인 contentDescription을 추가할 수 있습니다.Card( onClick = onClick, modifier = Modifier .fillMaxWidth() .semantics { contentDescription = "${buildOrderTitle(order)}, ${order.agencyName}, ${order.status}" }, colors = CardDefaults.cardColors(containerColor = backgroundCardColor()) ) { // ... 기존 내용 }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
app/src/main/java/com/sampoom/android/feature/order/ui/OrderListScreen.kt(1 hunks)app/src/main/java/com/sampoom/android/feature/order/ui/OrderListViewModel.kt(1 hunks)app/src/main/java/com/sampoom/android/feature/order/ui/OrderResultBottomSheet.kt(1 hunks)app/src/main/res/values/strings.xml(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- app/src/main/java/com/sampoom/android/feature/order/ui/OrderListViewModel.kt
- app/src/main/java/com/sampoom/android/feature/order/ui/OrderResultBottomSheet.kt
🧰 Additional context used
🧬 Code graph analysis (1)
app/src/main/java/com/sampoom/android/feature/order/ui/OrderListScreen.kt (5)
app/src/main/java/com/sampoom/android/core/ui/theme/Color.kt (3)
textColor(256-257)backgroundCardColor(253-254)textSecondaryColor(259-260)app/src/main/java/com/sampoom/android/core/ui/component/ErrorContent.kt (1)
ErrorContent(18-37)app/src/main/java/com/sampoom/android/core/ui/component/EmptyContent.kt (1)
EmptyContent(9-20)app/src/main/java/com/sampoom/android/core/util/OrderTitle.kt (1)
buildOrderTitle(6-26)app/src/main/java/com/sampoom/android/core/ui/component/StatusChip.kt (1)
StatusChip(18-37)
🔇 Additional comments (6)
app/src/main/res/values/strings.xml (4)
76-76: 이전 리뷰 코멘트가 반영되었습니다.타이포("목륵" → "목록")와 상태 매핑("주문실패" → "주문취소")이 모두 올바르게 수정되었습니다. 피드백을 반영해주셔서 감사합니다.
Also applies to: 78-78, 100-100
81-96: 주문 관리 문자열 추가가 일관성 있게 구성되었습니다.새로운 주문 관리 문자열들이 기존의
cart_*,outbound_*네이밍 패턴과 일치하고, 다이얼로그/토스트 메시지가 적절히 구조화되어 있습니다.
97-101: 주문 상태 문자열이 의미론적으로 적절합니다.각 상태별 문자열("승인대기", "입고완료", "주문취소")이 도메인 개념을 정확하게 반영하고 있습니다.
111-111: 리뷰 코멘트는 근거 없는 가정에 기반하고 있습니다.실제 코드 사용처 분석 결과,
common_slash는 데이터가 없을 때 표시하는 null 플레이스홀더 역할을 합니다:
OrderListScreen.kt:order.agencyName ?: stringResource(R.string.common_slash)OrderDetailContent.kt:order.orderNumber ?: stringResource(R.string.common_slash)"-" 문자는 이러한 "데이터 없음" 상황에서 UI에 표시하기에 적절한 값입니다. 코드베이스에 "/" 문자를 구분자로 사용하는 패턴이 없으며, 유사 리소스인
part_minus도 "-"를 값으로 사용하고 있습니다. 리소스 이름이 다소 혼동할 수 있다면common_placeholder또는common_na로 이름 변경을 고려할 수 있지만, 현재 값은 실제 사용 용도에 맞게 올바르게 설정되어 있습니다.Likely an incorrect or invalid review comment.
app/src/main/java/com/sampoom/android/feature/order/ui/OrderListScreen.kt (2)
117-122: 이전 리뷰 지적사항 수정 완료
forEach대신itemsDSL을 사용하도록 수정되어 LazyColumn의 지연 로딩이 올바르게 동작합니다. 성능 최적화가 적용되었습니다.
51-53: 이전 리뷰 지적사항 수정 완료중복 데이터 로드 이슈가 해결되었습니다.
LaunchedEffect에서LoadOrderList이벤트 호출을 제거하여 ViewModel의init블록에서만 초기 로드가 실행됩니다.
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (2)
app/src/main/java/com/sampoom/android/feature/order/ui/OrderDetailScreen.kt (2)
52-59: LaunchedEffect(취소 성공) 수정 반영 확인이전 지적 사항이 반영되어, 성공일 때만 clearSuccess와 재로딩이 트리거됩니다. 좋습니다.
61-68: LaunchedEffect(입고 성공) 수정 반영 확인위와 동일하게 조건부로 처리되어 부작용이 제거되었습니다.
🧹 Nitpick comments (1)
app/src/main/java/com/sampoom/android/feature/order/ui/OrderDetailScreen.kt (1)
120-147: 로딩/에러/빈 화면 높이 고정(200dp) 대신 전면 센터링 권장부분 높이 고정은 큰 화면에서 어색합니다. 전면 채우고 중앙 정렬이 더 자연스럽습니다.
@@ - Box( - modifier = Modifier - .fillMaxWidth() - .height(200.dp), + Box( + modifier = Modifier + .fillMaxSize(), contentAlignment = Alignment.Center ) { CircularProgressIndicator() } @@ - ErrorContent( - onRetry = { viewModel.onEvent(OrderDetailUiEvent.RetryOrder) }, - modifier = Modifier - .height(200.dp) - .fillMaxWidth() - ) + ErrorContent( + onRetry = { viewModel.onEvent(OrderDetailUiEvent.RetryOrder) }, + modifier = Modifier.fillMaxSize() + ) @@ - EmptyContent( - message = stringResource(R.string.order_empty_list), - modifier = Modifier - .height(200.dp) - .fillMaxWidth() - ) + EmptyContent( + message = stringResource(R.string.order_empty_list), + modifier = Modifier.fillMaxSize() + )
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
app/src/main/java/com/sampoom/android/feature/order/ui/OrderDetailScreen.kt(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
app/src/main/java/com/sampoom/android/feature/order/ui/OrderDetailScreen.kt (4)
app/src/main/java/com/sampoom/android/core/ui/component/CommonButton.kt (1)
CommonButton(49-176)app/src/main/java/com/sampoom/android/core/ui/component/ErrorContent.kt (1)
ErrorContent(18-37)app/src/main/java/com/sampoom/android/core/ui/component/EmptyContent.kt (1)
EmptyContent(9-20)app/src/main/java/com/sampoom/android/feature/order/ui/OrderDetailContent.kt (1)
OrderDetailContent(29-65)
🔇 Additional comments (1)
app/src/main/java/com/sampoom/android/feature/order/ui/OrderDetailScreen.kt (1)
41-41: @OptIn(ExperimentalMaterial3Api) 주석은 필수이며 제거할 수 없습니다프로젝트에서 사용 중인 Material3 1.1.2 버전에서는 TopAppBar가 여전히 실험 API로 표시되어 있습니다. Material3 1.1.2에서는 TopAppBar API가 여전히 experimental로 주석 처리되어 있으므로 @OptIn(ExperimentalMaterial3Api::class)을 사용해야 합니다. 해당 주석을 제거하면 컴파일 오류가 발생합니다. 검토 의견의 제안은 적용할 수 없습니다.
Likely an incorrect or invalid review comment.
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (1)
app/src/main/java/com/sampoom/android/feature/order/ui/OrderResultBottomSheet.kt (1)
60-62: LaunchedEffect 키 안정성 개선 확인
order.firstOrNull()?.orderId를 키로 사용한 점 좋습니다. 이전 불안정 키 문제를 적절히 해결했습니다.
🧹 Nitpick comments (10)
app/src/main/java/com/sampoom/android/feature/order/ui/OrderListViewModel.kt (1)
51-78: 이전 리뷰 피드백이 반영되었습니다Job 관리(
loadJob)와 I/O 디스패처(withContext(Dispatchers.IO))가 적절히 추가되어 경쟁 상태와 메인 스레드 블로킹 문제가 해결되었습니다.선택적 개선: Line 76의 로그가 오류 발생 시에도 실행됩니다. 필요시 try 블록 내부로 이동하는 것을 고려해보세요.
app/src/main/java/com/sampoom/android/feature/part/ui/PartListScreen.kt (2)
147-156: LazyColumn 항목에 key 지정으로 스크롤/상태 안정성 확보항목 키가 없어 재구성 시 스크롤 점프/상태 손실 위험이 있습니다. 고유한 코드로 key를 부여하세요.
- items(uiState.partList) { part -> + items(uiState.partList, key = { it.code }) { part -> PartListItemCard( part = part, onClick = {
50-50: 잘못된/불필요한 import 제거
OutboundListUiEvent는 사용되지 않으며, 파트 화면에는 부적합한 import입니다. 제거해 주세요.-import com.sampoom.android.feature.outbound.ui.OutboundListUiEventapp/src/main/java/com/sampoom/android/feature/outbound/ui/OutboundListScreen.kt (3)
52-52: 불필요한 import 제거
OrderListUiEvent는 본 파일에서 사용되지 않습니다. 제거하여 경고를 줄이세요.-import com.sampoom.android.feature.order.ui.OrderListUiEvent
171-176: Spacer 대신 LazyColumn contentPadding 사용하단 여유 공간을
item { Spacer(...) }로 넣기보다contentPadding을 사용하면 성능과 가독성이 좋아집니다.- LazyColumn( + LazyColumn( modifier = Modifier .fillMaxSize() .padding(horizontal = 16.dp), - verticalArrangement = Arrangement.spacedBy(16.dp) + verticalArrangement = Arrangement.spacedBy(16.dp), + contentPadding = PaddingValues(bottom = 100.dp) ) { @@ - item { Spacer(Modifier.height(100.dp)) }Also applies to: 191-191
177-189: Lazy 항목에 안정적 key 부여
item { ... }에 key가 없어 재구성 안정성이 떨어집니다. 카테고리/그룹 조합으로 key를 부여하세요.- uiState.outboundList.forEach { category -> - category.groups.forEach { group -> - item { + uiState.outboundList.forEach { category -> + category.groups.forEach { group -> + item(key = "${category.categoryName}:${group.groupName}") { OutboundSection( categoryName = category.categoryName, groupName = group.groupName,app/src/main/java/com/sampoom/android/feature/order/ui/OrderResultBottomSheet.kt (1)
3-3: 잘못된 플랫폼 import 제거
import android.R.attr.onClick는 Compose 코드에 불필요하며 오해를 유발합니다. 삭제하세요.-import android.R.attr.onClickapp/src/main/java/com/sampoom/android/feature/cart/ui/CartListScreen.kt (3)
50-52: 불필요한 import 정리아래 import는 사용되지 않습니다. 제거하여 경고를 줄이세요.
-import com.sampoom.android.feature.order.ui.OrderListUiEvent -import kotlin.collections.forEach
167-176: Spacer 대신 LazyColumn contentPadding 사용하단 여백을
contentPadding으로 처리해 리스트 성능과 가독성을 개선하세요.- LazyColumn( + LazyColumn( modifier = Modifier .fillMaxSize() .padding(horizontal = 16.dp), - verticalArrangement = Arrangement.spacedBy(16.dp) + verticalArrangement = Arrangement.spacedBy(16.dp), + contentPadding = PaddingValues(bottom = 100.dp) ) { @@ - item { Spacer(Modifier.height(100.dp)) }Also applies to: 186-186
172-186: Lazy 항목에 안정적 key 부여카테고리/그룹 아이템에 key가 없어 상태/스크롤 안정성이 떨어집니다. 조합 키를 부여하세요.
- uiState.cartList.forEach { category -> - category.groups.forEach { group -> - item { + uiState.cartList.forEach { category -> + category.groups.forEach { group -> + item(key = "${category.categoryName}:${group.groupName}") { CartSection(
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
app/src/main/java/com/sampoom/android/app/navigation/AppNavHost.kt(8 hunks)app/src/main/java/com/sampoom/android/core/util/FormatDate.kt(1 hunks)app/src/main/java/com/sampoom/android/feature/cart/ui/CartListScreen.kt(3 hunks)app/src/main/java/com/sampoom/android/feature/order/ui/OrderDetailScreen.kt(1 hunks)app/src/main/java/com/sampoom/android/feature/order/ui/OrderListScreen.kt(1 hunks)app/src/main/java/com/sampoom/android/feature/order/ui/OrderListViewModel.kt(1 hunks)app/src/main/java/com/sampoom/android/feature/order/ui/OrderResultBottomSheet.kt(1 hunks)app/src/main/java/com/sampoom/android/feature/outbound/ui/OutboundListScreen.kt(4 hunks)app/src/main/java/com/sampoom/android/feature/part/ui/PartListScreen.kt(3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- app/src/main/java/com/sampoom/android/feature/order/ui/OrderDetailScreen.kt
- app/src/main/java/com/sampoom/android/app/navigation/AppNavHost.kt
🧰 Additional context used
🧬 Code graph analysis (5)
app/src/main/java/com/sampoom/android/feature/part/ui/PartListScreen.kt (3)
app/src/main/java/com/sampoom/android/core/ui/component/ErrorContent.kt (1)
ErrorContent(18-37)app/src/main/java/com/sampoom/android/core/ui/component/EmptyContent.kt (1)
EmptyContent(9-20)app/src/main/java/com/sampoom/android/feature/part/domain/model/PartList.kt (1)
items(3-11)
app/src/main/java/com/sampoom/android/feature/cart/ui/CartListScreen.kt (5)
app/src/main/java/com/sampoom/android/feature/order/ui/OrderResultBottomSheet.kt (1)
OrderResultBottomSheet(46-149)app/src/main/java/com/sampoom/android/core/ui/theme/Color.kt (1)
textColor(256-257)app/src/main/java/com/sampoom/android/core/ui/component/ErrorContent.kt (1)
ErrorContent(18-37)app/src/main/java/com/sampoom/android/core/ui/component/EmptyContent.kt (1)
EmptyContent(9-20)app/src/main/java/com/sampoom/android/core/ui/component/CommonButton.kt (1)
CommonButton(49-176)
app/src/main/java/com/sampoom/android/feature/order/ui/OrderResultBottomSheet.kt (3)
app/src/main/java/com/sampoom/android/feature/order/ui/OrderDetailContent.kt (1)
OrderDetailContent(29-65)app/src/main/java/com/sampoom/android/core/ui/component/CommonButton.kt (1)
CommonButton(49-176)app/src/main/java/com/sampoom/android/core/ui/theme/Color.kt (1)
textColor(256-257)
app/src/main/java/com/sampoom/android/feature/outbound/ui/OutboundListScreen.kt (4)
app/src/main/java/com/sampoom/android/core/ui/theme/Color.kt (1)
textColor(256-257)app/src/main/java/com/sampoom/android/core/ui/component/ErrorContent.kt (1)
ErrorContent(18-37)app/src/main/java/com/sampoom/android/core/ui/component/EmptyContent.kt (1)
EmptyContent(9-20)app/src/main/java/com/sampoom/android/core/ui/component/CommonButton.kt (1)
CommonButton(49-176)
app/src/main/java/com/sampoom/android/feature/order/ui/OrderListScreen.kt (5)
app/src/main/java/com/sampoom/android/core/ui/theme/Color.kt (3)
textColor(256-257)backgroundCardColor(253-254)textSecondaryColor(259-260)app/src/main/java/com/sampoom/android/core/ui/component/ErrorContent.kt (1)
ErrorContent(18-37)app/src/main/java/com/sampoom/android/core/ui/component/EmptyContent.kt (1)
EmptyContent(9-20)app/src/main/java/com/sampoom/android/core/util/OrderTitle.kt (1)
buildOrderTitle(6-26)app/src/main/java/com/sampoom/android/core/ui/component/StatusChip.kt (1)
StatusChip(18-37)
🔇 Additional comments (6)
app/src/main/java/com/sampoom/android/feature/order/ui/OrderListScreen.kt (2)
49-155: 이전 리뷰 피드백이 모두 반영되었습니다
- LaunchedEffect에서 중복 로딩 트리거 제거 (Lines 62-64에서
bindLabel만 호출)- LazyColumn에서
items()DSL 사용으로 성능 최적화 (Line 143)- Pull-to-refresh와 상태별 UI 처리가 적절히 구현됨
전체적인 구조와 로직이 우수합니다.
157-204: LGTM!
OrderItem컴포저블이 깔끔하게 구현되었습니다. 유틸리티 함수(buildOrderTitle,formatDate)와 컴포넌트(StatusChip) 활용이 적절하며, null 안전성 처리도 잘 되어 있습니다.app/src/main/java/com/sampoom/android/feature/cart/ui/CartListScreen.kt (3)
70-75: 주문 결과 바텀시트 연동 👍
processedOrder존재 시 결과 바텀시트를 노출하고,onDismiss에서 상태를 정리하는 플로우가 명확합니다.
156-161: 문구 리소스 확인 요청
cart_empty_outbound리소스 키가 장바구니 빈 상태 문구로 맞는지 확인해 주세요. 오탈자/복붙 가능성이 있어 보입니다.
54-58: 리뷰 의견 확인 완료검증 결과,
CartListScreen함수 서명 변경에 따른 모든 호출부가 올바르게 업데이트되었음을 확인했습니다:
- 함수 정의:
app/src/main/java/com/sampoom/android/feature/cart/ui/CartListScreen.kt(라인 55-57)- 호출부:
app/src/main/java/com/sampoom/android/app/navigation/AppNavHost.kt(라인 167)
CartListScreen(paddingValues = innerPadding)✓코드베이스 전체에서
paddingValues인자 없이 호출하는 경우가 없으므로, 변경사항이 완전히 적용되었습니다.app/src/main/java/com/sampoom/android/feature/outbound/ui/OutboundListScreen.kt (1)
57-60: 서명 변경이 올바르게 반영됨: paddingValues 인자 전달 확인 완료검증 결과, 유일한 호출 사이트(
app/src/main/java/com/sampoom/android/app/navigation/AppNavHost.kt:162)에서paddingValues = innerPadding으로 인자를 올바르게 전달하고 있습니다. 컴파일 에러는 발생하지 않습니다.
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
app/src/main/java/com/sampoom/android/feature/order/ui/OrderListViewModel.kt (1)
38-40: 초기 로드 중복 호출 문제 지속이전 리뷰에서 지적된 것처럼, ViewModel의
init블록과 UI의LaunchedEffect가 모두loadOrderList()를 호출하여 중복 로드가 발생합니다. init 블록을 제거하고 UI에서만 이벤트를 통해 로드를 트리거하도록 일원화하세요.
🧹 Nitpick comments (5)
app/src/main/java/com/sampoom/android/feature/order/ui/OrderListViewModel.kt (2)
70-70: 코드 스타일 일관성 개선닫는 괄호 앞에 불필요한 공백이 있습니다.
- orderError = backendMessage ?: (throwable.message ?: errorLabel ) + orderError = backendMessage ?: (throwable.message ?: errorLabel)
74-74: 프로덕션 로그 검토 권장디버그 로그가 프로덕션 빌드에도 남아있을 수 있습니다. 팀의 로깅 정책에 따라 제거하거나 조건부로 활성화하는 것을 고려하세요.
app/src/main/java/com/sampoom/android/feature/part/ui/PartListScreen.kt (2)
50-50: 사용하지 않는 import 제거 권장
OutboundListUiEvent를 파일 내에서 사용하지 않습니다. 불필요한 import는 제거하는 것이 좋습니다.-import com.sampoom.android.feature.outbound.ui.OutboundListUiEvent
103-110: 로딩 UX 패턴 개선 권장현재
uiState.partListLoading이 true일 때 pull-to-refresh 인디케이터와 전체 화면CircularProgressIndicator가 동시에 표시됩니다. 일반적인 pull-to-refresh UX 패턴은 다음과 같습니다:
- 초기 로딩: 중앙에 로딩 인디케이터 표시
- 새로고침: 기존 리스트를 유지하고 상단의 pull 인디케이터만 표시
현재 구현은 새로고침 중에도 리스트를 숨기고 전체 화면 로딩을 표시하므로, 초기 로딩과 새로고침을 구분하는 상태를 추가하여 UX를 개선하는 것을 권장합니다.
app/src/main/java/com/sampoom/android/core/util/FormatDate.kt (1)
8-29: 엣지 케이스에 대한 단위 테스트 추가를 권장합니다다양한 입력 형식(오프셋 유무, 'T' 누락, 잘못된 형식 등)에 대한 테스트를 추가하면 함수의 견고성을 검증할 수 있습니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
app/src/main/java/com/sampoom/android/core/util/FormatDate.kt(1 hunks)app/src/main/java/com/sampoom/android/feature/order/ui/OrderListViewModel.kt(1 hunks)app/src/main/java/com/sampoom/android/feature/part/ui/PartListScreen.kt(3 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
app/src/main/java/com/sampoom/android/feature/part/ui/PartListScreen.kt (2)
app/src/main/java/com/sampoom/android/core/ui/component/ErrorContent.kt (1)
ErrorContent(18-37)app/src/main/java/com/sampoom/android/core/ui/component/EmptyContent.kt (1)
EmptyContent(9-20)
🔇 Additional comments (3)
app/src/main/java/com/sampoom/android/feature/order/ui/OrderListViewModel.kt (1)
49-76: 이전 리뷰 지적사항이 적절히 반영되었습니다경쟁 상태 방지(
loadJob활성 체크)와 I/O 디스패처 사용(withContext(Dispatchers.IO))이 모두 구현되어, 이전 리뷰에서 제기된 주요 우려사항들이 해결되었습니다.CancellationException처리도 올바릅니다.app/src/main/java/com/sampoom/android/feature/part/ui/PartListScreen.kt (1)
104-110: 이전 리뷰 이슈 해결 확인 ✅이전 리뷰에서 지적된 innerPadding 누락 문제가 모두 해결되었습니다. 로딩, 에러, 빈 상태, 리스트 모든 콘텐츠 브랜치에
.padding(innerPadding)이 올바르게 적용되어 상단 앱바와의 겹침 문제가 해결되었습니다.Also applies to: 113-123, 126-136, 139-157
app/src/main/java/com/sampoom/android/core/util/FormatDate.kt (1)
19-26: 잘 구현되었습니다선택적 소수점 초(0-6자리) 처리가 포함되어 다양한 ISO 형식을 유연하게 파싱할 수 있습니다.
📝 Summary
🙏 Question & PR point
📬 Reference
Summary by CodeRabbit
새로운 기능
개선 사항
버그 수정